package admin.redis.submit.interceptor;

import admin.redis.submit.annot.PreventRepeatSubmit;
import admin.redis.submit.utils.RequestUtils;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.util.StringUtils;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.HandlerInterceptor;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.TimeUnit;

/**
 * @Description: TODO
 * @Author: heky
 * @CreateDate: 2024/2/6 16:38
 */
public class PreventRepeatSubmitInterceptor implements HandlerInterceptor {
    @Value("${token.header:sa-token}")
    private String header;
    public final String REPEAT_PARAMS = "repeatParams";
    public final String REPEAT_TIME = "repeatTime";

    @Autowired
    private ObjectMapper objectMapper;

    @Autowired
    private RedisTemplate redisTemplate;

    public PreventRepeatSubmitInterceptor() {
    }

    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        if (handler instanceof HandlerMethod) {
            HandlerMethod handlerMethod = (HandlerMethod)handler;
            Method method = handlerMethod.getMethod();
            PreventRepeatSubmit annotation = (PreventRepeatSubmit)method.getAnnotation(PreventRepeatSubmit.class);
            if (annotation != null && this.isRepeatSubmit(request, annotation)) {
                String unAuthorizedResponse = "{\"code\":9002,\"msg\":\"" + annotation.message() + "\"}";
                response.getWriter().write(unAuthorizedResponse);
                return false;
            } else {
                return true;
            }
        } else {
            return true;
        }
    }

    public boolean isRepeatSubmit(HttpServletRequest request, PreventRepeatSubmit annotation) throws JsonProcessingException {
        String nowParams = "";
        if (request instanceof RepeatedlyRequestWrapper) {
            RepeatedlyRequestWrapper repeatedlyRequest = (RepeatedlyRequestWrapper)request;
            nowParams = RequestUtils.getBodyString(repeatedlyRequest);
        }

        if (StringUtils.hasLength(nowParams)) {
            nowParams = this.objectMapper.writeValueAsString(request.getParameterMap());
        }

        Map<String, Object> nowDataMap = new HashMap(4);
        nowDataMap.put(REPEAT_PARAMS, nowParams);
        nowDataMap.put(REPEAT_TIME, System.currentTimeMillis());
        String url = request.getRequestURI();
        String submitKey = request.getHeader(this.header);
        String cacheRepeatKey = "RepeatSummit" + url + submitKey;
        Object sessionObj = this.redisTemplate.opsForValue().get(cacheRepeatKey);
        if (sessionObj != null) {
            Map<String, Object> sessionMap = (Map)sessionObj;
            if (sessionMap.containsKey(url)) {
                Map<String, Object> preDataMap = (Map)sessionMap.get(url);
                if (this.compareParams(nowDataMap, preDataMap) && this.compareTime(nowDataMap, preDataMap, annotation.interval())) {
                    return true;
                }
            }
        }

        Map<String, Object> cacheMap = new HashMap(4);
        cacheMap.put(url, nowDataMap);
        this.redisTemplate.opsForValue().set(cacheRepeatKey, cacheMap, annotation.interval(), TimeUnit.MILLISECONDS);
        return false;
    }

    private boolean compareParams(Map<String, Object> nowMap, Map<String, Object> preMap) {
        String nowParams = (String)nowMap.get(REPEAT_PARAMS);
        String preParams = (String)preMap.get(REPEAT_PARAMS);
        return nowParams.equals(preParams);
    }

    private boolean compareTime(Map<String, Object> nowMap, Map<String, Object> preMap, int interval) {
        long time1 = (Long)nowMap.get(REPEAT_TIME);
        long time2 = (Long)preMap.get(REPEAT_TIME);
        return time1 - time2 < (long)interval;
    }
}